package app

import (
	"errors"
	"fmt"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"gluenet/internal/glue-srvm/pkg/apm"
	"gluenet/internal/glue-srvm/pkg/gluehttp"
	"gluenet/internal/glue-srvm/pkg/gluelog"
	"net/http"
	"strings"
	"time"
)

// CORSMiddleware Enable cross domain
func CORSMiddleware() gin.HandlerFunc {
	return cors.New(cors.Config{
		AllowMethods:     []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
		AllowAllOrigins:  true,
		AllowCredentials: true,
	})
}

// TraceMiddleware Add traceId to facilitate tracking log and user response correspondence
func TraceMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		tranceId := apm.GetTraceId()
		c.Set(gluehttp.TranceIdKey, tranceId)
		gluelog.Infof("request begin")
		c.Next()
		gluelog.Infof("request end")
	}
}

// LoggerMiddleware Log
func LoggerMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		raw := c.Request.URL.RawQuery
		if raw != "" {
			path = path + "?" + raw
		}

		c.Next()
		end := time.Now()
		latency := end.Sub(start)

		msg := "Request"
		if len(c.Errors) > 0 {
			msg = c.Errors.String()
		}

		msgBuilder := strings.Builder{}
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", gluehttp.TranceIdKey, c.GetString(gluehttp.TranceIdKey)))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%d ", "status", c.Writer.Status()))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", "method", c.Request.Method))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", "path", path))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", "ip", c.ClientIP()))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", "latency", latency))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", "user-agent", c.Request.UserAgent()))
		msgBuilder.WriteString(fmt.Sprintf(" %s:%s ", "msg", msg))

		switch {
		case c.Writer.Status() >= http.StatusBadRequest && c.Writer.Status() < http.StatusInternalServerError:
			{
				gluelog.Warningf(msgBuilder.String())
			}
		case c.Writer.Status() >= http.StatusInternalServerError:
			{
				gluelog.Errorf(msgBuilder.String())
			}
		default:
			gluelog.Infof(msgBuilder.String())
		}

	}
}

// ErrorMiddleware Global exception handling
func ErrorMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		ctx.Next()
		if err, exists := ctx.Get(gluehttp.GlobalError); exists {
			var httpError *gluehttp.ResponseError
			if errors.As(err.(error), &httpError) || errors.As(err.(error), httpError) {
				gluelog.Errorf("%+v", httpError.Err)
				gluehttp.ErrorResponse(ctx, *httpError)
				ctx.Abort()
				return
			}
			if responseErr, ok := err.(error); ok {
				httpError = gluehttp.NewError(900, "未知异常", responseErr)
				gluelog.Errorf("%+v", httpError.Err)
				gluehttp.ErrorResponse(ctx, *httpError)
				ctx.Abort()
				return
			}
		}
	}
}
